/*
 * VoltageDividerCircuit.cpp
 *
 * Copyright 2012 Pieter Agten
 *
 * This file is part of tltgen.
 *
 * tltgen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * tltgen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with tltgen.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file   VoltageDividerCircuit.cpp
 * @author Pieter Agten (pieter.agten@gmail.com)
 * @date   18 mar 2012
 */

#include <stdexcept>
#include <boost/lexical_cast.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/export.hpp>

#include "Temperature.hpp"
#include "ThermistorModel.hpp"
#include "Voltage.hpp"
#include "CircuitVisitor.hpp"

#include "VoltageDividerCircuit.hpp"

namespace tltgen {

  VoltageDividerCircuit::VoltageDividerCircuit(Voltage vin, Resistance rdiv, Resistance rpar) :
    vin(vin), rdiv(rdiv), rpar(rpar)
  {
    if (! isValidInputVoltage(vin)) {
      throw std::invalid_argument("Voltage '" + boost::lexical_cast<std::string>(vin) + "' is not a valid input voltage for this circuit.");
    }
    if (! isValidDividerResistance(rdiv)) {
      throw std::invalid_argument("Resistance '"+ boost::lexical_cast<std::string>(rdiv) +"' is not a valid divider resistance for this circuit.");
    }
    if (! isValidParallelResistance(rpar)) {
      throw std::invalid_argument("Resistance '"+ boost::lexical_cast<std::string>(rdiv) +"' is not a valid parallel resistance for this circuit.");
    }
  }

  VoltageDividerCircuit::~VoltageDividerCircuit() { };


  bool VoltageDividerCircuit::isValidInputVoltage(Voltage vin)
  {
    return tltgen::isValidVoltage(vin) && vin >= 0;
  }

  bool VoltageDividerCircuit::isValidDividerResistance(Resistance rdiv)
  {
    return tltgen::isValidResistance(rdiv) && std::isfinite(rdiv);
  }

  bool VoltageDividerCircuit::isValidParallelResistance(Resistance rpar)
  {
    return tltgen::isValidResistance(rpar);
  }


  Voltage VoltageDividerCircuit::getInputVoltage() const
  {
    return this->vin;
  }

  Resistance VoltageDividerCircuit::getDividerResistance() const
  {
    return this->rdiv;
  }

  bool VoltageDividerCircuit::hasParallelResistor() const
  {
    return this->rpar != tltgen::openCircuitResistance();
  }

  Resistance VoltageDividerCircuit::getParallelResistance() const
  {
    return this->rpar;
  }


  bool VoltageDividerCircuit::isValidOutputVoltage(Voltage voltage) const
  {
    return tltgen::isValidVoltage(voltage) && voltage >= 0 && voltage <= this->vin;
  }

  Resistance VoltageDividerCircuit::getThermistorResistance(Voltage Vout) const
  {
    if (! isValidOutputVoltage(Vout)) {
      throw std::invalid_argument("Voltage '" + boost::lexical_cast<std::string>(Vout) + 
				  "' is not a valid output voltage for this circuit.");
    }
    Voltage Vin = this->getInputVoltage();
    Resistance Rdiv = this->getDividerResistance();
    Resistance Rpar = this->getParallelResistance();

    Resistance Rt;
    if (Rpar == tltgen::openCircuitResistance()) {
      Rt = Rdiv * Vout / (Vin - Vout);
    } else {
      Rt = -Vout * Rdiv * Rpar / (Vout*(Rdiv + Rpar) - Vin*Rpar);
    }
    return Rt;
  }


  bool VoltageDividerCircuit::isValidThermistorResistance(Resistance r) const
  {
    return tltgen::isValidResistance(r);
  }

  Voltage VoltageDividerCircuit::getOutputVoltage(Resistance Rt) const
  {
    if (! isValidThermistorResistance(Rt)) {
      throw std::invalid_argument("Resistance '" + boost::lexical_cast<std::string>(Rt) + 
				  "' is not a valid thermistor resistance for this circuit.");
    }
    Voltage Vin = this->getInputVoltage();
    Resistance Rdiv = this->getDividerResistance();
    Resistance Rpar = this->getParallelResistance();
    Resistance Rbottom;
    if (Rpar == tltgen::openCircuitResistance()) {
      Rbottom = Rt;     
    } else {
      Rbottom = (Rt * Rpar)/(Rt + Rpar);
    }

    Voltage v = Vin * Rbottom / (Rbottom + Rdiv);
    return v;
  }

  Power VoltageDividerCircuit::getThermistorPowerDissipationAtResistance(Resistance Rt) const
  {
    Voltage Vout = getOutputVoltage(Rt);
    return pow(Vout, 2)/Rt;
  }

  Power VoltageDividerCircuit::getThermistorPowerDissipationAtOutputVoltage(Voltage Vout) const
  {
    Resistance Rt = getThermistorResistance(Vout);
    return pow(Vout, 2)/Rt;
  }

  void VoltageDividerCircuit::accept(CircuitVisitor* visitor) const
  {
    visitor->visit(this);
  }

}

BOOST_CLASS_EXPORT_IMPLEMENT(tltgen::VoltageDividerCircuit)
